﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Liang.Util.Task.Enums;

namespace Liang.Util.Task
{
	/// <summary>
	/// 抽象基类
	/// </summary>
	public abstract class ITask
	{
		#region 私有成员
		/// <summary>
		/// 定时器
		/// </summary>
		private Timer SysTimer = null;
		/// <summary>
		/// 定时器策略
		/// </summary>
		private TimerStrategy _TimerStrategy = null;
		/// <summary>
		/// 系统启动参数
		/// </summary>
		private object[] Params = null;
		#endregion

		#region 公共属性
		/// <summary>
		/// 任务执行状态
		/// </summary>
		public TaskScheduleStatus TaskStatus { get; private set; }
		/// <summary>
		/// 任务的计划
		/// </summary>
		public TimerStrategy Schedule { get { return _TimerStrategy; } set { TimeCalculations.CheckStrategy(value); _TimerStrategy = value; } }
		#endregion

		/// <summary>
		/// 停止
		/// </summary>
		public void Stop()
		{
			try
			{
				OnStop();
			}
			catch { }

			if (SysTimer != null)
			{
				SysTimer.Change(Timeout.Infinite, Timeout.Infinite);
				SysTimer.Dispose();
				SysTimer = null;
			}

			TaskStatus = TaskScheduleStatus.Stopped;
		}

		/// <summary>
		/// 开始服务
		/// </summary>
		/// <param name="strategy">时间策略</param>
		/// <param name="Params">调用参数</param>
		public void Start(TimerStrategy strategy, params object[] parameters)
		{
			try
			{
				TimeCalculations.CheckStrategy(strategy);
				this.Params = parameters;
				this.Schedule = strategy;
				if (this.Schedule.Delay == null)
					this.Schedule.Delay = new TimeSpan(0);

				SysTimer = new Timer(new TimerCallback(this.TimerOnceRunning), AppDomain.CurrentDomain, this.Schedule.Delay, this.Schedule.Interval);
                //SysTimer.Elapsed += new System.Timers.ElapsedEventHandler(driver_Elapsed);
			}
			catch (Exception ex)
			{
				this.OnThrowException(ex);
			}
		}

		/// <summary>
		/// 单次执行服务程序
		/// </summary>
		public void RuningOnce()
		{
			try
			{
				//开始处理服务
				this.OnStart(this.Params);
			}
			catch (Exception ex) { this.OnThrowException(ex); }	// 处理服务执行过程中出现的异常
		}

		/// <summary>
		/// 处理间隔服务
		/// </summary>
		/// <param name="sender"></param>
		private void TimerOnceRunning(object sender)
		{
			bool TimeIsUp = true;
			if (this.Schedule.TimerMode != TimerStrategyMode.Interval)
			{
				// 如果定时方式不是定时轮询的话，就构造TimerControl类，该类用来计算每次执行完程序后
				// 到下次执行服务时需要休眠的时间

				/// 判断对象是否为空，是则构造控制对象
				TimeIsUp = TimeCalculations.TimeIsUp(this.Schedule);	// 获取是否到了执行服务程序的时间了
			}

			try
			{
				if (TimeIsUp)// 时间到了可以执行程序了
				{
					// 服务运行了
					TaskStatus = TaskScheduleStatus.Running;

					// 设置计时器，在无穷时间后再启用（实际上就是永远不启动计时器了--停止计时器计时）
					SysTimer.Change(Timeout.Infinite, Timeout.Infinite);

					//开始处理服务
					if (this.Schedule.Reentrant)
						new Action(() =>
						{
							try
							{
								this.OnStart(this.Params);
							}
							catch (Exception ex) { this.OnThrowException(ex); }	// 处理服务执行过程中出现的异常
						}).BeginInvoke(null, null);
					else
						this.OnStart(this.Params);
				}
			}
			catch (Exception ex) { this.OnThrowException(ex); }	// 处理服务执行过程中出现的异常
			finally
			{
				// 如果计时器不为空，则重新设置休眠的时间
				if (SysTimer != null)
				{
					if (this.Schedule.TimerMode == TimerStrategyMode.Interval)// 定时轮询设置
					{
						// 重新启用计时器
						SysTimer.Change(this.Schedule.Interval, this.Schedule.Interval);
					}
					else// 定时设置
					{
						// 用cft类计算下一次到期的时间
						var Interval = TimeCalculations.GetNextTimeUp(this.Schedule);
						if (Interval.Ticks <= 0)
						{// 可能是过早的醒过来了，导致这次获取的时间点，还是本次应该执行的时间点，而实际上，当前时间已经比目标时间大了，导致间隔时间为负，因此需要重新计算时间
							Interval = TimeCalculations.GetNextTimeUp(this.Schedule);
						}

						// 重新启用计时器
						SysTimer.Change(Interval, Interval);
					}
				}
				TaskStatus = TaskScheduleStatus.Completed;
			}
		}

		#region 抽象的方法
		/// <summary>
		/// 开始任务
		/// </summary>
		/// <param name="Params"></param>
		protected abstract void OnStart(params object[] Params);
		/// <summary>
		/// 停止任务
		/// </summary>
		protected abstract void OnStop();
		/// <summary>
		/// 产生异常
		/// </summary>
		/// <param name="ex"></param>
		protected abstract void OnThrowException(Exception ex);
		#endregion
	}
}
